package jnpf.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jnpf.common.enums.dify.DifyApiEnum;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static jnpf.common.statics.ApiStatic.*;


@Slf4j
public class DifyApiReqUtil {

    private static final OkHttpClient client;

    static {
        client = new OkHttpClient.Builder()
                // 连接超时
                .connectTimeout(1000 * 50, TimeUnit.MILLISECONDS)
                // 读取超时
                .readTimeout(1000 * 50, TimeUnit.MILLISECONDS)
                // 写入超时
                .writeTimeout(1000 * 50, TimeUnit.MILLISECONDS).build();
    }

    /**
     * 根据给定的枚举类型对应的操作发送请求，并返回响应内容
     *
     * @param url         dify路径
     * @param apiKey      请求目标的key
     * @param apiEnum     代表不同Dify API操作的枚举值
     * @param params      请求参数（以键值对形式，用于填充请求路径中的占位符等，根据不同操作传入对应参数）
     * @param requestBody 请求体内容（如果是POST等需要请求体的请求方法时传入，JSON格式字符串等，可为null）
     * @param userId
     * @return 返回服务器响应的内容字符串
     * @throws IOException 如果请求过程中出现网络相关或其他I/O异常则抛出
     */
    public static String sendApiReq(String url, String apiKey, DifyApiEnum apiEnum, Map<String, String> params, Object requestBody, String userId) throws IOException {
        log.info("请求key:{}", apiKey);

        String endpoint = getFullEndpoint(apiEnum, params);
        String apiUrl = url + endpoint;
        Request.Builder requestBuilder = new Request.Builder().url(apiUrl);
        requestBuilder.addHeader(AUTHORIZATION_HEADER, BEARER_PREFIX + apiKey).addHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_VALUE);
        Request request = buildRequest(apiEnum, requestBuilder, requestBody);

        log.info("请求地址:{}", apiUrl);
        log.info("请求目标:{}", apiEnum.getDes());
        log.info("请求路径:{}", endpoint);
        StringBuilder headerStr = new StringBuilder();
        Headers headers = request.headers();
        for (String name : headers.names()) {
            headerStr.append(name).append(": ").append(headers.get(name)).append(";");
        }
        log.info("请求头: {}", headerStr.toString());
        if (requestBody != null) {
            log.info("请求体:{}", requestBody.toString());
        }
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                return response.body().string();
            } else {
                throw new IOException(response.body().string());
            }
        }
    }

    public static String sendFileRequest(String url, String apiKey, File file, String userId) throws IOException {
        log.info("请求key: {}", apiKey);
        log.info("请求目标: {}", DifyApiEnum.CREATE_CHAT_MESSAGE.getDes());
        log.info("请求路径: {}", url);
        log.info("请求头: {}", BEARER_PREFIX + apiKey);

        MultipartBody.Builder multipartBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);

        // 添加 user 字段
        multipartBuilder.addFormDataPart("user", userId);

        if (file != null && file.exists()) {
            String fileName = file.getName();
            // 使用通用的二进制数据类型
            RequestBody fileBody = RequestBody.create(file, MediaType.parse("application/octet-stream"));
            multipartBuilder.addFormDataPart("file", fileName, fileBody);
        }

        MultipartBody multipartBody = multipartBuilder.build();

        Request request = new Request.Builder().url(url).addHeader(AUTHORIZATION_HEADER, BEARER_PREFIX + apiKey).post(multipartBody).build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                return response.body().string();
            } else {
                log.info("请求失败: {}", response.body().string());
                throw new IOException(response.body().string());
            }
        }
    }

    /**
     * 流式请求
     *
     * @param url         拼接好的请求路径
     * @param apiKey      请求key
     * @param requestBody 请求体
     * @return
     * @throws Exception
     */
    public static Flux<String> sendStreamingRequest(String url, String apiKey, Object requestBody) {
        log.info("请求key:{}", apiKey);
        log.info("请求目标:{}", DifyApiEnum.CREATE_CHAT_MESSAGE.getDes());
        log.info("请求路径:{}", url);
        log.info("请求头:{}", BEARER_PREFIX + apiKey);
        WebClient webClient = WebClient.builder().baseUrl(url).defaultHeader(AUTHORIZATION_HEADER, BEARER_PREFIX + apiKey).defaultHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_VALUE).build();
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonRequestBody = null;
        try {
            jsonRequestBody = objectMapper.writeValueAsString(requestBody);
        } catch (JsonProcessingException e) {
            log.error("请求体序列化失败", e);
            throw new RuntimeException(e);
        }
        log.info("请求体:{}", jsonRequestBody);
        return webClient.post().uri(url).contentType(org.springframework.http.MediaType.APPLICATION_JSON).body(BodyInserters.fromObject(jsonRequestBody)).retrieve().onStatus(HttpStatus::is5xxServerError, clientResponse -> {
            log.error("服务器返回5xx: {}", clientResponse.statusCode());
            return clientResponse.bodyToMono(String.class).flatMap(errorBody -> Mono.error(new RuntimeException("服务器错误: " + errorBody)));
        }).bodyToFlux(String.class).doOnNext(responsePart -> {
            log.info("接收到的响应部分: {}", responsePart);
        });

    }

    private static String getFullEndpoint(DifyApiEnum apiEnum, Map<String, String> params) {
        String endpoint = apiEnum.getApi();
        if (params == null) {
            return endpoint;
        }
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (entry.getValue() == null) {
                endpoint = endpoint.replace("{" + entry.getKey() + "}", "");
            } else {
                endpoint = endpoint.replace("{" + entry.getKey() + "}", entry.getValue());
            }
        }
        return endpoint;
    }

    private static Request buildRequest(DifyApiEnum apiEnum, Request.Builder requestBuilder, Object requestBody) {
        String requestMethod = apiEnum.getApiType();
        if ("GET".equals(requestMethod)) {
            return requestBuilder.build();
        } else if ("POST".equals(requestMethod)) {
            try {
                ObjectMapper objectMapper = new ObjectMapper();
                String jsonRequestBody = objectMapper.writeValueAsString(requestBody);
                RequestBody body = RequestBody.create(jsonRequestBody, MediaType.parse(CONTENT_TYPE_VALUE));
                return requestBuilder.post(body).build();
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                throw new IllegalArgumentException("请求体序列化失败，请检查对象格式是否正确", e);
            }
        } else if ("PUT".equals(requestMethod)) {
            try {
                ObjectMapper objectMapper = new ObjectMapper();
                String jsonRequestBody = objectMapper.writeValueAsString(requestBody);
                RequestBody body = RequestBody.create(jsonRequestBody, MediaType.parse(CONTENT_TYPE_VALUE));
                return requestBuilder.put(body).build();
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                throw new IllegalArgumentException("请求体序列化失败，请检查对象格式是否正确", e);
            }
        } else if ("DELETE".equals(requestMethod)) {
            if (requestBody != null) {
                try {
                    ObjectMapper objectMapper = new ObjectMapper();
                    String jsonRequestBody = objectMapper.writeValueAsString(requestBody);
                    RequestBody body = RequestBody.create(jsonRequestBody, MediaType.parse(CONTENT_TYPE_VALUE));
                    return requestBuilder.delete(body).build();
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                    throw new IllegalArgumentException("请求体序列化失败，请检查对象格式是否正确", e);
                }
            } else {
                return requestBuilder.delete().build();
            }
        }
        throw new IllegalArgumentException("不支持的请求方法: " + requestMethod);
    }

    public static String sendApiReq(String url, String apiKey, DifyApiEnum apiEnum, Map<String, String> params, Object requestBody) throws IOException {
        log.info("请求key:{}", apiKey);

        String endpoint = getFullEndpoint(apiEnum, params);
        String apiUrl = url + endpoint;
        Request.Builder requestBuilder = new Request.Builder().url(apiUrl);
        requestBuilder .addHeader(CONTENT_TYPE_HEADER, CONTENT_TYPE_VALUE)
                    .addHeader(AUTHORIZATION_HEADER,apiKey);

        Request request = buildRequest(apiEnum, requestBuilder, requestBody);

        log.info("请求地址:{}", apiUrl);
        log.info("请求目标:{}", apiEnum.getDes());
        log.info("请求路径:{}", endpoint);
        StringBuilder headerStr = new StringBuilder();
        Headers headers = request.headers();
        for (String name : headers.names()) {
            headerStr.append(name).append(": ").append(headers.get(name)).append(";");
        }
        log.info("请求头: {}", headerStr.toString());
        if (requestBody != null) {
            log.info("请求体:{}", requestBody.toString());
        }
//        String s = generateCurlCommand(url, apiKey, apiEnum.getApiType(), requestBody);
//        System.out.println(s);
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                return response.body().string();
            } else {
                return response.body().string();
//                throw new IOException(response.body().string());
            }
        }
    }
    /**
     * 生成curl命令字符串
     */
    private static String generateCurlCommand(String url, String apiKey, String method, Object requestBody) {
        StringBuilder curl = new StringBuilder("curl -X ").append(method)
                .append(" '").append(url).append("' \\\n")
                .append("  -H '").append(AUTHORIZATION_HEADER).append(": ").append(BEARER_PREFIX).append(apiKey).append("' \\\n")
                .append("  -H '").append(CONTENT_TYPE_HEADER).append(": ").append(CONTENT_TYPE_VALUE).append("'");

        if (requestBody != null && ("POST".equals(method) || "PUT".equals(method) || "DELETE".equals(method))) {
            try {
                String jsonBody = new ObjectMapper().writeValueAsString(requestBody);
                curl.append(" \\\n  -d '").append(jsonBody).append("'");
            } catch (JsonProcessingException e) {
                log.error("生成curl命令时序列化请求体失败", e);
            }
        }

        return curl.toString();
    }
}
